#pragma once

#include <iostream>
#include <cstring>
#include <cerrno>
#include <strings.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "err.hpp"

class TcpClient
{
    static const size_t BUF_SIZE = 1024;

public:
    TcpClient(uint16_t svr_port, std::string svr_ip)
        : _svr_port(svr_port), _svr_ip(svr_ip)
    {
        bzero(&_svr, sizeof(_svr));
    }

    void Initial()
    {
        // 1.创建套接字
        if ((_sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        {
            std::cerr << "socket create fail: " << strerror(errno) << std::endl;
            exit(SOCKET_ERR);
        }

        // 由OS自动绑定ip和port

        // 2.指定客户端地址族
        _svr.sin_family = AF_INET;
        _svr.sin_port = htons(_svr_port);
        _svr.sin_addr.s_addr = inet_addr(_svr_ip.c_str());
    }

    void Start()
    {
        // try to connect the server
        int times = 5;
        while (times > 0 && connect(_sockfd, (struct sockaddr *)&_svr, sizeof(_svr)) < 0)
        {
            std::cout << "正在尝试重新连接服务器..." << times-- << std::endl;
            sleep(1);
        }
        if (times == 0)
        {
            std::cout << "连接失败!" << std::endl;
            exit(CONNECT_ERR);
        }
        // connect success
        std::cout << "连接成功!" << std::endl;

        while (true)
        {
            // 1.向服务端发送消息
            std::string message;
            std::cout << "ENTER:> ";
            std::getline(std::cin, message);

            ssize_t n = write(_sockfd, message.c_str(), message.size());
            if (n < 0)
            {
                // write fail
                std::cerr << "write to client fail: " << strerror(errno) << std::endl;
                exit(WRITE_ERR);
            }

            // 2.接收服务端返回的消息
            char buf[BUF_SIZE];
            memset(buf, 0, sizeof(buf));
            n = read(_sockfd, buf, sizeof(buf) - 1);
            if (n < 0)
            {
                // read fail
                std::cerr << "read from server fail: " << strerror(errno) << std::endl;
                exit(READ_ERR);
            }
            else if (n == 0)
            {
                std::cout << "server already closed" << std::endl;
                exit(0);
            }
            else
            {
                buf[n] = '\0';
                std::cout << "server sent to: " << buf << std::endl;
            }
        }
    }

private:
    int _sockfd;
    uint16_t _svr_port;
    std::string _svr_ip;
    struct sockaddr_in _svr;
};